package main

import (
	"context"
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	_ "github.com/spf13/viper/remote"
	"gopkg.in/yaml.v2"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func SetDefaultDemo() {
	viper.SetDefault("ContentDir", "content")
	viper.SetDefault("LayoutDir", "layouts")
	viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
}

//Viper会按照下面的优先级。每个项目的优先级都高于它下面的项目:
//显示调用Set设置值
//命令行参数（flag）
//环境变量
//配置文件
//key/value存储
//默认值
func ReadConf(path string) {

	viper.SetConfigFile(path)

	// 如果配置文件中的名称没有扩展名则需要指定扩展名
	//viper.SetConfigType("yaml")

	// 查找配置文件所在的路径
	// 多次调用添加多个搜索路径
	viper.AddConfigPath(".")

	if err := viper.ReadInConfig(); err != nil {
		panic(fmt.Errorf("Fatal error config file: %v , err:%s \n", path, err))
	}

	s := viper.AllSettings()
	bs, _ := yaml.Marshal(s)

	fmt.Printf("config settings  %s \n", string(bs))

	// 实时监控配置文件变化
	viper.WatchConfig()
	viper.OnConfigChange(func(in fsnotify.Event) {
		fmt.Printf("config file changed %s \n", in.Name)
	})

	viper.AutomaticEnv()
	viper.SetEnvPrefix("APP_ENV_") // 设置读取的环境变量前缀
	viper.BindEnv("version")

}

func ReadConfRemote() {
	// 或者你可以创建一个新的viper实例
	var runtime_viper = viper.New()

	runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
	runtime_viper.SetConfigType("yaml") // 因为在字节流中没有文件扩展名，所以这里需要设置下类型。支持的扩展名有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"

	// 第一次从远程读取配置
	if err := runtime_viper.ReadRemoteConfig(); err != nil {
		panic(fmt.Errorf("Fatal error config file err:%s \n", err))
	}

	// 反序列化
	runtime_viper.Unmarshal(&runtime_viper)

	// 开启一个单独的goroutine一直监控远端的变更
	go func() {
		for {
			time.Sleep(time.Second * 5) // 每次请求后延迟一下

			// 目前只测试了etcd支持
			err := runtime_viper.WatchRemoteConfig()
			if err != nil {
				fmt.Errorf("unable to read remote config: %v", err)
				continue
			}

			// 将新配置反序列化到我们运行时的配置结构体中。你还可以借助channel实现一个通知系统更改的信号
			runtime_viper.Unmarshal(&runtime_viper)
		}
	}()
}

func main() {

	// 学习资料
	// https://liwenzhou.com/posts/Go/viper_tutorial/

	viper.SetDefault("ss", "sss")
	SetDefaultDemo()
	ReadConf("./conf.yaml")
	ReadConf("./conf.json")

	// 访问嵌套
	viper.GetString("datastore.metric.host")

	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "Welcome Gin Server")
	})

	router.GET("/version", func(c *gin.Context) {
		c.String(http.StatusOK, viper.GetString("version"))
	})

	srv := &http.Server{
		Addr:    ":8080",
		Handler: router,
	}

	// 优雅停机 学习资料
	// https://liwenzhou.com/posts/Go/graceful_shutdown/
	go func() {
		// 开启一个goroutine启动服务
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 等待中断信号来优雅地关闭服务器，为关闭服务器操作设置一个5秒的超时
	quit := make(chan os.Signal, 1) // 创建一个接收信号的通道
	// kill 默认会发送 syscall.SIGTERM 信号
	// kill -2 发送 syscall.SIGINT 信号，我们常用的Ctrl+C就是触发系统SIGINT信号
	// kill -9 发送 syscall.SIGKILL 信号，但是不能被捕获，所以不需要添加它
	// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞
	<-quit                                               // 阻塞在此，当接收到上述两种信号时才会往下执行
	log.Println("Shutdown Server ...")
	// 创建一个5秒超时的context
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	// 5秒内优雅关闭服务（将未处理完的请求处理完再关闭服务），超过5秒就超时退出
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown: ", err)
	}

	log.Println("Server exiting")
}
